package org.chen.config;

import io.netty.channel.ChannelOption;
import io.netty.channel.ConnectTimeoutException;
import io.netty.handler.timeout.ReadTimeoutException;
import io.netty.handler.timeout.ReadTimeoutHandler;
import io.netty.handler.timeout.WriteTimeoutHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpHeaders;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.web.reactive.function.client.ClientRequest;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.netty.http.client.HttpClient;
import reactor.netty.resources.ConnectionProvider;
import reactor.util.retry.Retry;

import java.nio.channels.UnresolvedAddressException;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.List;
import java.util.Random;
import java.util.concurrent.TimeUnit;

@Configuration
@Slf4j
public class HttpClientConfig {
    private static final List<String> USER_AGENTS = List.of(
            "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36",
            "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36",
            "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36",
            "Mozilla/5.0 (iPhone; CPU iPhone OS 15_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.4 Mobile/15E148 Safari/604.1",
            "Mozilla/5.0 (iPad; CPU OS 14_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.6 Mobile/15E148 Safari/604.1",
            "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Edge/107.0.1418.56"
    );

    @Value("${webclient.max-connections:300}")
    private Integer maxConnections;

    @Value("${webclient.acquisition-timeout:30000}")
    private Integer acquisitionTimeout;

    @Value("${webclient.connection-timeout:30000}")
    private Integer connectionTimeout;

    @Value("${webclient.read-timeout:30000}")
    private Integer readTimeout;

    @Bean
    public ConnectionProvider connectionProvider() {
        return ConnectionProvider.builder("custom")
                .maxConnections(maxConnections)
                .pendingAcquireTimeout(Duration.ofMillis(acquisitionTimeout))
                .pendingAcquireMaxCount(-1)
                .maxIdleTime(Duration.ofMillis(readTimeout))
                .build();
    }

    @Bean
    public WebClient webClient(ConnectionProvider connectionProvider) {
        HttpClient httpClient = HttpClient.create(connectionProvider)
                .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, connectionTimeout)
                .option(ChannelOption.SO_KEEPALIVE, true)
                .responseTimeout(Duration.ofMillis(readTimeout))
                .doOnConnected(conn -> conn
                        .addHandlerLast(new ReadTimeoutHandler(readTimeout, TimeUnit.MILLISECONDS))
                        .addHandlerLast(new WriteTimeoutHandler(readTimeout, TimeUnit.MILLISECONDS)))
                .resolver(spec -> spec
                        .queryTimeout(Duration.ofSeconds(5)));

        return WebClient.builder()
                .clientConnector(new ReactorClientHttpConnector(httpClient))
                .codecs(configurer -> configurer
                        .defaultCodecs()
                        .maxInMemorySize(16 * 1024 * 1024))
                .filter((request, next) -> {
                    // 动态设置User-Agent
                    String randomUserAgent = USER_AGENTS.get(new Random().nextInt(USER_AGENTS.size()));
                    ClientRequest newRequest = ClientRequest.from(request)
                            .header("User-Agent", randomUserAgent)
                            .build();

                    // 添加重试逻辑
                    return next.exchange(newRequest)
                            .retryWhen(Retry.backoff(3, Duration.ofSeconds(1))
                                    .filter(throwable ->
                                            throwable instanceof UnresolvedAddressException ||
                                                    throwable instanceof ConnectTimeoutException ||
                                                    throwable instanceof ReadTimeoutException)
                                    .doBeforeRetry(retrySignal ->
                                            log.warn("Retrying request after error: {}, attempt: {}",
                                                    retrySignal.failure().getMessage(),
                                                    retrySignal.totalRetries() + 1)));
                })
                .defaultHeader(HttpHeaders.ACCEPT, "*/*")
                .defaultHeader(HttpHeaders.ACCEPT_CHARSET, StandardCharsets.UTF_8.name())
                .build();
    }
}
